#!/usr/bin/python

import socket
import select

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(('', 2007))
server_socket.listen(5)
# server_socket.setblocking(0)
poll = select.poll() # epoll() should work the same
poll.register(server_socket.fileno(), select.POLLIN)

connections = {} # 首先定义一个从文件描述符到socket对象的映射
while True: # 程序的主体是一个事件循环
    events = poll.poll(10000)  # 10 seconds 每当有IO事件发生时，就针对不同的文件描述符(fileno)执行不同的操作
    for fileno, event in events:
        if fileno == server_socket.fileno(): # 对于listening fd，接受(accept)新连接，并注册到IO事件关注列表(watch list)，然后把连接添加到connections字典中
            (client_socket, client_address) = server_socket.accept()
            print "got connection from", client_address
            # client_socket.setblocking(0)
            poll.register(client_socket.fileno(), select.POLLIN)
            connections[client_socket.fileno()] = client_socket
        elif event & select.POLLIN: # 对于客户连接，则读取并回显数据，并处理连接的关闭
            client_socket = connections[fileno]
            data = client_socket.recv(4096)
            if data:
                client_socket.send(data) # sendall() partial? 没有开启non-blocking，也没有考虑数据发送不完整. 对于echo服务而言，真正的业务逻辑只有L28：将收到的数据原样发回客户端
            else:
                poll.unregister(fileno)
                client_socket.close()
                del connections[fileno]